#pragma once
#include <iostream>
#include <fstream>
#include "Tabuleiro.h"
#include "Pedra.h"
#include "Aluminio.h"
#include "TerrenoMole.h"
#include "TerrenoDuro.h"
#include "Carvao.h"
#include "Ferro.h"
#include "Ouro.h"
#include "Diamante.h"
#include "BlocoVazio.h"
#include "VigaBloco.h"
#include "EscadaBloco.h"

using namespace std;

void Tabuleiro::allocMemory(int altura, int largura)
{
	tabuleiro = new Bloco**[altura];
	for(int i = 0; i < altura; i++)
	{
		tabuleiro[i] = new Bloco*[largura];
		for(int j = 0; j < largura; j++)
		{
			tabuleiro[i][j] = NULL;
		}
	}

	this->altura = altura;
	this->largura = largura;
}

void Tabuleiro::criaBloco(int tipo, int i, int j)
{
	switch(tipo)
	{
	case 0:
		addBloco(new Pedra(i, j), i, j);
		break;
	case 1:
		addBloco(new TerrenoMole(i, j), i, j);
		break;
	case 2:
		addBloco(new TerrenoDuro(i, j), i, j);
		break;
	case 3:
		addBloco(new Aluminio(i, j), i, j);
		break;
	case 4:
		addBloco(new Carvao(i, j), i, j);
		break;
	case 5:
		addBloco(new Ferro(i, j), i, j);
		break;
	case 6:
		addBloco(new Ouro(i, j), i, j);
		break;
	case 7:
		addBloco(new Diamante(i, j), i, j);
		break;
	case 9:
		addBloco(new EscadaBloco(i, j), i, j);
		break;
	case 10:
		addBloco(new VigaBloco(i, j), i, j);
		break;
	case 12:
		addBloco(new BlocoVazio(i, j), i, j);
		break;
	}
}

Tabuleiro::Tabuleiro(string nome, int altura, int largura, int dificuldade)
{
	tempo = 0;
	double r;
	allocMemory(altura, largura);

	for(int i = 0; i < altura; i++)
	{
		for(int j = 0; j < largura; j++)
		{
			if(i == 0)
			{
				addBloco(new BlocoVazio(i, j), i, j);
			}
			else
			{
				r = ((double) rand() / (RAND_MAX));

				if(r > 0.0 && r < 0.01)
				{
					addBloco(new Diamante(i, j), i, j);
				}
				if(r > 0.01 && r < 0.02)
				{
					addBloco(new BlocoVazio(i, j), i, j);
				}
				if(r > 0.02 && r < 0.15)
				{
					addBloco(new Pedra(i, j), i, j);
				}
				if(r > 0.15 && r < 0.35)
				{
					addBloco(new TerrenoDuro(i, j), i, j);
				}
				if(r > 0.35 && r < 0.60)
				{
					addBloco(new TerrenoMole(i, j), i, j);
				}
				if(r > 0.60 && r < 0.70)
				{
					addBloco(new Aluminio(i, j), i, j);
				}
				if(r > 0.70 && r < 0.75)
				{
					addBloco(new Carvao(i, j), i, j);
				}
				if(r > 0.75 && r < 0.79)
				{
					addBloco(new Ferro(i, j), i, j);
				}
				if(r > 0.79 && r < 0.82)
				{
					addBloco(new Ouro(i, j), i, j);
				}
				if(r > 0.82 && r < 1.0)
				{
					addBloco(new TerrenoMole(i, j), i, j);
				}
			}
		}
	}

	addMineiro(tabuleiro[0][0]);
}

Tabuleiro::Tabuleiro(string nome_ficheiro)
{
	tempo = 0;
	ifstream ifs(nome_ficheiro);

	if (ifs.is_open())
	{
		ifs >> altura;
		ifs >> largura;

		allocMemory(altura, largura);

		int tipo;

		for(int i = 0; i < altura; i++)
		{
			for(int j = 0; j < largura; j++)
			{
				ifs >> tipo;
				criaBloco(tipo, i, j);
			}
		}

		int posX;
		int posY;

		ifs >> posX;
		ifs >> posY;

		addMineiro(tabuleiro[posX][posY]);
	}
}

Tabuleiro::~Tabuleiro(void)
{
}

Bloco*** Tabuleiro::getTabuleiro()
{
	return tabuleiro;
}

int Tabuleiro::getAltura()
{
	return altura;
}

int Tabuleiro::getLargura()
{
	return largura;
}

void Tabuleiro::addBloco(Bloco* bloco, int x, int y)
{
	tabuleiro[x][y] = bloco;
}

void Tabuleiro::addMineiro(Bloco* posicao)
{
	mineiro = new Mineiro(posicao);
}

void Tabuleiro::setMineiro(Bloco* posicao)
{
	mineiro->setPosicao(posicao);
}

Mineiro* Tabuleiro::getMineiro()
{
	return mineiro;
}

Bloco* Tabuleiro::switchBloco(Bloco* blocoOriginal, int y, int x)
{
	delete blocoOriginal;
	blocoOriginal = new BlocoVazio(x, y);
	addBloco(blocoOriginal, x, y);
	return blocoOriginal;
}

bool Tabuleiro::aplicaGravidade()
{
	int numBlocosCaidos = 1;
	int posicaoMineiroLinha = mineiro->getPosicao()->getLinha();
	int posicaoMineiroColuna = mineiro->getPosicao()->getColuna();
	int posicaoPossivelBlocoVazio = posicaoMineiroLinha + 1;
	Bloco* bloco = tabuleiro[posicaoPossivelBlocoVazio][posicaoMineiroColuna];

	while(bloco->aplicaGravidade())
	{
		mineiro->mover(bloco);
		posicaoPossivelBlocoVazio++;
		numBlocosCaidos++;

		if(posicaoPossivelBlocoVazio < altura)
		{
			bloco = tabuleiro[posicaoPossivelBlocoVazio][posicaoMineiroColuna];
		}
	}

	mineiro->cai((numBlocosCaidos - 1) * 10);

	if(mineiro->getEnergiaMineiro() < 1)
		return false;

	return true;
}

void Tabuleiro::aplicaGravidadePedra(Tabuleiro* tab)
{
	int posicaoMineiroLinha = mineiro->getPosicao()->getLinha();
	int posicaoMineiroColuna = mineiro->getPosicao()->getColuna();
	int posicaoBlocoLinha = mineiro->getPosicao()->getLinha() - 1;
	Bloco* bloco = tab->getTabuleiro()[posicaoMineiroLinha][posicaoMineiroColuna];
	Bloco* blocoAcima = tab->getTabuleiro()[posicaoBlocoLinha][posicaoMineiroColuna];

	while(blocoAcima->aplicaGravidadePedra())
	{
		if(posicaoBlocoLinha >= 0)
		{
			delete blocoAcima;
			blocoAcima = new BlocoVazio(posicaoBlocoLinha, posicaoMineiroColuna);
			delete bloco;
			bloco = new Pedra(posicaoMineiroLinha, posicaoMineiroColuna);
			posicaoBlocoLinha--;
		}
		else
			break;
	}
}

int Tabuleiro::getTempo()
{
	return tempo;
}

void Tabuleiro::avancaTempo()
{
	tempo++;
}

bool Tabuleiro::verificaVida()
{
	if(mineiro->getEnergia()->getEnergia() < 1)
	{
		delete mineiro;
		return false;
	}
	return true;
}

Bloco* Tabuleiro::aplicaViga(Bloco* blocoOriginal, int x, int y)
{
	delete blocoOriginal;
	blocoOriginal = new VigaBloco(x, y);
	addBloco(blocoOriginal, x, y);
	return blocoOriginal;
}

Bloco* Tabuleiro::aplicaEscada(Bloco* blocoOriginal, int x, int y)
{
	delete blocoOriginal;
	blocoOriginal = new EscadaBloco(x, y);
	addBloco(blocoOriginal, x, y);
	return blocoOriginal;
}